灯泡 - CS50x 2023
并非完全损坏的灯泡
在课堂上,你可能已经注意到舞台前方似乎存在一个“bug”,其中一些灯泡似乎总是熄灭:
然而,每个灯泡序列都以二进制编码了一条消息,二进制是计算机说的语言。我们来编写一个程序来制作自己的秘密消息,说不定我们还能搬到舞台上去呢!
开始
打开 VS Code。
首先点击终端窗口,然后输入 cd
。你应该发现它的提示符类似于下面。
单击该终端窗口内部,然后执行
wget https://cdn.cs50.net/2022/fall/psets/2/bulbs.zip
然后按回车键,在你的 codespace 中下载名为 bulbs.zip
的 ZIP 文件。注意 wget
和 URL 之间有空格,别漏掉了,其他字符也一样!
现在执行
来创建一个名为 bulbs
的文件夹。现在可以删除 ZIP 文件了,输入
然后输入 y
并按回车键删除下载的 ZIP 文件。
现在输入
然后按回车键进入该目录。提示符应该类似下面这样。
如果没问题,输入
并看到一个名为 bulbs.c
的文件。code bulbs.c
会打开文件,你可以在里面写代码。如果没有,请回溯你的步骤,看看你是否可以确定你哪里出错了!
实现细节
要编写我们的程序,我们首先需要考虑进制。
基础知识
最简单的进制是 1 进制,或一元; 要用 1 进制写一个数字 N,我们只需写 N 个连续的 1
。 因此,1 进制中的数字 4
将写为 1111
,数字 12
写为 111111111111
。 可以把它想象成用手指计数或用木板上的标记来计算分数。
你可能会明白为什么现在很少使用 1 进制。(数字变得相当长!) 相反,一个常见的约定是 10 进制,或十进制。 在十进制中,每个数字都乘以 10 的某个幂,以便表示更大的数字。 例如, 是 的简写。
更改进制就像将上面更改为不同的数字一样简单。 例如,如果你用四进制写 123
,那么它实际上表示 ,等于十进制的 。
然而,计算机使用 2 进制,或二进制。 在二进制中,写 123
将是一个错误,因为二进制数只能有 0
和 1
。 但是,弄清楚二进制数代表的确切十进制数的过程是完全相同的。 例如,二进制数 10101
表示 ,它等于十进制数 。
编码消息
灯泡只能亮或灭。 换句话说,灯泡代表两种可能的状态; 灯泡要么亮,要么灭,就像二进制数要么是 1,要么是 0。 我们必须找到一种方法将文本编码为二进制数序列。
我们来写一个叫 bulbs
的程序,它可以把一段文字转换成灯泡的亮灭状态,展示给观众。我们将分两步完成:
- 第一步包括将文本转换为十进制数。 比如我们要编码消息
HI!
。 幸运的是,我们已经有了一个约定来做到这一点,ASCII。H
的十进制表示是72
,I
是73
,!
是33
。 - 下一步包括获取我们的十进制数(如
72
、73
和33
)并将它们转换为等效的二进制数,这些二进制数仅使用 0 和 1。 为了统一位数,我们用 8 位二进制数来表示每个十进制数。72
是01001000
,73
是01001001
,33
是00100001
。
最后,我们将把这些二进制数解释为舞台上灯泡的指令; 0 是关闭,1 是打开。 bulbs.c
里有一个已经写好的 print_bulb
函数,输入 0
或 1
就能输出代表灯泡的 emoji。
这是一个已完成程序可能如何工作的示例。 和 Sanders 舞台不一样,为了更清楚,我们每行打印一个字节。
检查结果时,亮的灯泡 () 代表
1
,灭的灯泡 () 代表
0
。 那么 HI!
变成了
01001000
01001001
00100001
这正是我们所期望的。
另一个例子:
请注意,所有字符都包含在灯泡指令中,包括非字母字符,如空格 (00100000
)。
规范
设计并实现一个程序 bulbs
,将文本转换成 CS50 舞台灯条的控制指令,具体要求如下:
- 请在名为
bulbs.c
的文件中实现你的程序。 - 你的程序首先需要使用
get_string
函数提示用户输入信息。 - 接下来,你的程序需要将输入的字符串 (
string
) 转换为一系列8位二进制数,每个字符对应一个二进制数。 - 你可以使用提供的
print_bulb
函数打印一系列0
和1
,这些数字会以黄色和黑色 emoji 的形式显示,分别代表灯泡的亮和灭。 - 每个包含 8 个符号的“字节”在输出时应单独占一行;并且在最后一个“字节”后也需要添加一个换行符 (
\n
)。
十进制转二进制的提示
我们以数字 4 为例进行说明。 你将如何将 4 转换为二进制? 首先考虑最右边的位,如果该位为 on,则会将 1 添加到我们所表示的数字。 你需要打开这个位吗? 将 4 除以 2 找出答案:
2 可以整除 4,这告诉我们没有余数 1 需要担心。 因此,我们可以安全地将最右边的位关闭:
那么,现在,前面的位,也就是我们发现的这个位左边的那个位呢? 为了检查,让我们按照类似的过程,但从我们离开的地方继续。 在上一步中,我们将 4 除以 2 得到 2。 现在,2 是否能整除 2 呢? 答案是可以,因此无需考虑余数 2。
让我们继续进一步。 将 2 除以 2 后,我们剩下 1。 将 1 除以 2 留下余数 1。 这意味着我们需要打开这个位:
现在我们已经将我们的数字除到 0,我们不再需要任何位来表示它。 注意,我们发现这些代表数字 4 的二进制位顺序,与最终需要打印的顺序正好相反:我们可能需要一个结构来让我们存储这些位,以便我们以后可以向前打印它们。 当然,在你的实际代码中,你将使用 8 位的 char
,因此你需要预先添加任何需要的 0。
在计算余数时,取模运算符 (%
) 会很有用! 例如,4 % 2
返回 0,这意味着 2 除以 4 的余数为 0。
如何测试你的代码
你的程序应该能像上述示例一样运行。 你可以使用 check50
检查你的代码,check50
是 CS50 在你提交时用来测试你的代码的程序,方法是在 $
提示符下输入以下内容。 但务必自己也测试一下!
check50 cs50/problems/2023/x/bulbs
要评估你的程序代码风格,请在 $
提示符下输入以下内容。
如何提交
在你的终端中,执行以下命令来提交你的工作。
submit50 cs50/problems/2023/x/bulbs